import string

from kivy.logger import Logger

from amgineirc.viewModel.viewModel import ViewModel
from amgineirc.viewModel.settings.vmHelper.selectFolderVMHelper import SelectFolderVMHelper
from amgineirc.viewModel.settings.vmHelper.selectFileVMHelper import SelectFileVMHelper

class RulesViewModel(ViewModel):

    good_chars = string.letters + string.digits + '_'
    
    def __init__(self, view, appdata):
        ViewModel.__init__(self, view)
        self._appdata = appdata
        rules = appdata['rules'].data
        for i in rules:
            self.view.vm_add_rule(i)
        view.vm_option_command_handler = self.handle_option_command
            
    def handle_option_command(self, command, name):
        """handle a command for a rule (delete, edit)
        """
        from amgineirc.view.settings.rulesView import RulesView
        if command == RulesView.edit_command:
            # the user wants to edit a rule.
            self._edit_amgine(name)
        elif command == RulesView.delete_command:
            # the user wants to delete a rule.
            self._delete_amgine_check_used(name)
        elif command == RulesView.create_command:
            # the user wants to create a rule.
            self._create_amgine()
        elif command == RulesView.export_command:
            # the user wants to export a rule.
            self.w_export_amgine_check_name(name)
        elif command == RulesView.import_command:
            # the user wants to import a rule.
            self.import_amgine_browse_select()
            
    # rule create or edit
    
    def _create_amgine(self):
        """The user wants to create a new rotary encryption rule.
        """
        from amgineirc.view.settings.rulesView import RulesView, EditRulePopup
        popup = EditRulePopup(RulesView.create_command,
                              '',
                              self.w_handle_editor_command)
        popup.open()
        
    def _edit_amgine(self, name):
        """The user wants to edit an existing rotary encryption rule.
        """
        from amgineirc.view.settings.rulesView import RulesView, EditRulePopup
        popup = EditRulePopup(RulesView.edit_command,
                              name,
                              self.w_handle_editor_command)
        popup.open()
        
    def w_handle_editor_command(self, command, old_name, new_name):
        """The user has submitted the rotary encryption rule.
        """
        from amgineirc.view.settings.rulesView import RulesView
        msg = []
        if command == RulesView.submit_command:
            if len(new_name) == 0:
                msg.append('Rotary Encryption Rule Name is required.')
            elif new_name == old_name:
                msg.append('You did not edit the Rotary Encryption Rule Name.')
            else:
                bad_chars = False
                for c in new_name:
                    if c not in RulesViewModel.good_chars:
                        msg.append('Rotary Encryption Rule Name must be only letters and digits and underscores ( "_" ).')
                        bad_chars = True
                        break
                if bad_chars is False:
                    if len(old_name) == 0 and new_name in self._appdata['rules'].data:
                        # the new_name is valid
                        # there is no old_name so the user is creating a new rule.
                        # however a rule named new_name already exists
                        msg.append("A Rotary Encryption Rule named '%s' already exists." % (new_name))
            # check for settings folder
            if not self._appdata['rules'].have_configuration_folder():
                msg.append(self._appdata['rules'].missing_configuration_folder_message)
            ok = len(msg) == 0
            if ok:
                # backup the rules incase there is an error.
                backup = self._appdata['rules'].backup()
                if len(old_name) > 0:
                    # rename old_name to new_name
                    ok, smsg = self._appdata['rules'].set(old_name, new_name)
                    if ok is True:
                        self.view.vm_show_rule_edited(old_name, new_name)
                    else:
                        # error restore the rules
                        self._appdata['rules'].restore(backup)
                    
                else:
                    # adding a new rule named new_name.
                    ok, smsg = self._appdata['rules'].create(new_name)
                    if ok is True:
                        self.view.vm_show_rule_added(new_name)
                    else:
                        # error restore the rules
                        self._appdata['rules'].restore(backup)
                msg.append(smsg)
        return (ok, "\n".join(msg))
            
    # rule delete
    
    def _delete_amgine_check_used(self, name):
        """The user has selected a rule.
        It can't be deleted if it is being used.
        If not used then have the user confirm the deletion.
        """
        nick = self._appdata['trusted_users'].nick_using_rule(name)
        if nick is not None:
            # don't delete because the rule is used
            self.view.message_warn('You can not delete the rule named "%s" because you and "%s" are using it for DCC.' % (name, nick))
        else:
            self.view.question_yes_no('Delete a Rotary Encryption Rule.',
                                      'Are you sure you want to permanently delete the rotary encryption rule named ' + name + '?',
                                      self.w_delete_amgine_confirm,
                                      name)
        
    def w_delete_amgine_confirm(self, truefalse, name):
        # the user has confirmed wanting to delete a rule
        if truefalse is True:
            # check for settings folder
            ok = self._appdata['rules'].have_configuration_folder()
            if not ok:
                smsg = self._appdata['rules'].missing_configuration_folder_message
            else:
                backup = self._appdata['rules'].backup()
                ok, smsg = self._appdata['rules'].unlink(name)
                if not ok:
                    self._appdata['rules'].restore(backup)
            if ok:
                self.view.vm_show_rule_deleted(name)
            else:
                self.view.message_warn(smsg)
                                     
    # rule export
    
    def w_export_amgine_check_name(self, name):
        """The user has selected a rule to export.
        Make sure that the rule exists then let the user select a destination path.
        """
        # check for the settings folder
        ok = self._appdata['rules'].have_configuration_folder()
        if not ok:
            warning = self._appdata['rules'].missing_configuration_folder_message
            self.view.message_warn(warning)
        else:                          
            # the settings folder is there so check for the rule file.
            amgine = self._appdata['rules'].get(name)
            if amgine is None:
                warning = 'Unable to find the rule named "%s".' % (name) 
                self.view.message_warn(warning)
            else: 
                SelectFolderVMHelper(self)('Export the rule "%s"' % (name),
                                           self.w_export_amgine_check_destination_path,
                                           amgine, name)

    def w_export_amgine_check_destination_path(self, path, amgine, name):
        """The user has selected a destination path for the rotary encryption rule.
        Attempt to export the rule and inform the user of the results.
        """
        ok, message = self._appdata['rules'].export_rule_to(amgine, name, path)
        if ok:
            self.view.message_success(message)
        else:
            self.view.message_warn(message)
    
    # rule import
    
    def import_amgine_browse_select(self):
        """Allow the user to browse for a rotary encryption rule.
        """
        filter = '*' + self._appdata['rules'].fn_ext
        import_help = 'Make sure that you have the stick or card containing the import rule inserted into your device.' \
            '\n\nIf not then insert the stick or card and touch the "Reload" button below.' \
            '\n\nYou can always come back to this screen if you inserted the wrong stick or card.'
        select_file_help = 'If you want to insert another stick or card' \
            ' then click on the "Back" button before you insert the stick or card.'
        SelectFileVMHelper(self)('Import a rotary encryption rule.',
                                 import_help,
                                 select_file_help,
                                 [filter],
                                 self.w_import_amgine_check_source_path)
                                     
    def w_import_amgine_check_source_path(self, path):
        """The user has selected a rotary encryption rule.
        Check the path and the rule.
        If no rule is found inform the user.
        If a rule is found then continue.
        """
        amgine, message = self._appdata['rules'].load_rule_from(path)
        if amgine is not None:
            name = self._appdata['rules'].name_from_path(path) 
            self.w_import_amgine_recheck_name(name, path, amgine)
        else:
            # its not an amgine file
            self.view.message_warn(message,
                                   back_handler=self.import_amgine_browse_select)
            
    def w_import_amgine_recheck_name(self, name, path, amgine):
        """The user has selected a valid rule file.
        If the name needs changed then let the user change it.
        """
        if name in self._appdata['rules'].data:
            ok = False
            message = 'A rule named "%s" already exists.' % (name)
        elif self._appdata['rules'].is_valid_name(name) == False:
            ok = False
            message = '"%s" is not a valid name.' % (name)
        else:
            ok = True
            message = ''
        if not ok:              
            # have the user change the file name
            message += ' You must rename the rule from "%s" to a different name using letters and digits and underscores ( "_" ) only.' % (name) 
            self.view.message_edit_line('Rename %s' % (name),
                                        message,
                                        name, # text to edit
                                        self.w_import_amgine_recheck_name, # this method
                                        path, amgine) # largs
        else:
            # there should be no errors and the import should work.
            self.w_import_amgine_check_destination_path(name, path, amgine)
        
            
    def w_import_amgine_check_destination_path(self, name, path, amgine):
        """The user has selected a valid rotary encryption file and a usable name.
        Have the user make sure that the configuration folder is accessable if needed.
        """
        if self._appdata['rules'].have_configuration_folder():
            # add the rules
            ok, message = self._appdata['rules'].import_rule(amgine, name)
            if ok:
                # add the new rule name to the list view
                self.view.vm_show_rule_added(name)
            else:
                self.view.message_warn(message)
        else:
            # the settings folder is missing
            import_help_format = '%s\n\nThen press continue.'
            import_help = import_help_format % (self._appdata['rules'].missing_configuration_folder_message)
            self.view.select_options('Import a rotary encryption rule.',
                                     ['Continue'],
                                     self.w_import_amgine_recheck_destination_path, # handler
                                     name, path, amgine, # handler largs
                                     help=import_help)

    def w_import_amgine_recheck_destination_path(self, command, name, path, amgine):
        """The user has selected a valid rotary encryption file and a usable name.
        The configuration folder is accessable.
        Save the encryption rule to the configuration folder.
        """
        self.w_import_amgine_check_destination_path(name, path, amgine)
